En este documento aprenderemos a realizar transformaciones geométricas básicas sobre imágenes utilizando OpenCV en Python.
Estas operaciones son esenciales en el preprocesamiento de imágenes antes de aplicar técnicas más avanzadas.
Transformaciones que cubriremos:
Redimensionamiento (resize)
Recorte (crop)
Rotación
Traslación (desplazamiento)
Configuración inicial
Code
import cv2import numpy as npimport matplotlib.pyplot as pltfrom pathlib import Path# Función auxiliar para mostrar imágenes en color o grisesdef mostrar_img(img, titulo="Imagen", cmap=None): plt.figure(figsize=(5,5))iflen(img.shape) ==2: # Escala de grises plt.imshow(img, cmap='gray')else: # Color (BGR → RGB) plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) plt.title(titulo) plt.axis('off') plt.show()# Ruta a la carpeta de imágenes (ajustar según tu equipo)carpeta = Path("imagenes")# Cargar imagen de ejemploimg = cv2.imread(str(carpeta /"frutas.jpg"))mostrar_img(img, "Imagen original")
Redimensionamiento (Resize)
El redimensionamiento cambia el tamaño de una imagen mediante un escalamiento en coordenadas.
Si una imagen \(I\) tiene ancho \(w\) y alto \(h\), y queremos una imagen nueva \(I'\) de tamaño \(w' \times h'\), definimos factores:
Sin embargo, al construir la imagen de salida, OpenCV (y la mayoría de librerías) usan un mapeo inverso:
para cada pixel \((x',y')\) en la imagen resultante, se calcula de dónde proviene en la imagen original:
Como \(x\) y \(y\) suelen no ser enteros, se necesita interpolación para estimar el valor del pixel:
INTER_NEAREST: vecino más cercano (rápido, puede pixelar y dar aliasing).
INTER_LINEAR: bilineal (común para ampliar).
INTER_AREA: promediado por área (recomendado para reducir; actúa como antialiasing).
Code
# Imagen al doble de tamañoimg_doble = cv2.resize(img, None, fx=2, fy=2, interpolation=cv2.INTER_LINEAR)mostrar_img(img_doble, "Doble tamaño")# Imagen a la mitadimg_mitad = cv2.resize(img, None, fx=0.5, fy=0.5, interpolation=cv2.INTER_AREA)mostrar_img(img_mitad, "Mitad de tamaño")plt.imshow(cv2.cvtColor(img_doble, cv2.COLOR_BGR2RGB))plt.axis('off')plt.show()plt.imshow(cv2.cvtColor(img_mitad, cv2.COLOR_BGR2RGB))plt.axis('off')plt.show()print(img.shape) # Dimensiones originalesprint(img_doble.shape) # Dimensiones de la imagen al dobleprint(img_mitad.shape) # Dimensiones de la imagen a la mitad
(670, 1072, 3)
(1340, 2144, 3)
(335, 536, 3)
Recorte (Crop)
Un crop (corte) consiste en extraer una Región de Interés (ROI) de la imagen.
Si la imagen original \(I\) tiene dimensiones \(h \times w\) (alto por ancho), entonces un crop rectangular definido por:
filas: desde \(y_0\) hasta \(y_1\)
columnas: desde \(x_0\) hasta \(x_1\)
se expresa como una submatriz:
\[
I_{\text{crop}} = I[y_0:y_1,\; x_0:x_1]
\]
En Python/NumPy, los intervalos son semiabiertos: incluye \(y_0\) y excluye \(y_1\) (igual para \(x\)).
Esto produce una imagen con dimensiones:
\[
h' = y_1 - y_0,\qquad w' = x_1 - x_0
\]
Implementación (NumPy/OpenCV)
Code
# Recorte de una región específica (y1:y2, x1:x2)crop = img[100:500, 200:600]mostrar_img(crop, "Recorte 400x400")
Rotación
Una rotación en 2D transforma un punto \((x,y)\) a \((x',y')\).
Si rotamos un ángulo \(\theta\) alrededor del origen, la transformación se expresa como:
En imágenes, normalmente no queremos rotar respecto al origen \((0,0)\), sino respecto al centro\((c_x, c_y)\).
Para rotar alrededor del centro, se realiza esta idea:
Trasladar el centro al origen: \((x,y)\to(x-c_x, y-c_y)\)
Rotar con \(R(\theta)\)
Regresar el origen al centro: \((x,y)\to(x+c_x, y+c_y)\)
Esa combinación se puede escribir como una transformación afín:
\[
\begin{bmatrix}
x' \\
y'
\end{bmatrix}
=
\begin{bmatrix}
a & b & t_x \\
c & d & t_y
\end{bmatrix}
\begin{bmatrix}
x \\
y \\
1
\end{bmatrix}
\]
En el caso de rotación con escala \(s\), OpenCV usa:
\[
a = s\cos\theta,\quad b = s\sin\theta,\quad
c = -s\sin\theta,\quad d = s\cos\theta
\]
y el vector de traslación \((t_x,t_y)\) se elige para que el centro \((c_x,c_y)\) permanezca fijo:
\[
t_x = (1-a)c_x - b c_y,\qquad
t_y = b c_x + (1-d)c_y
\]
Code
# Obtener dimensiones(h, w) = img.shape[:2]centro = (w //2, h //2)# Matriz de rotación (10 grados)M_rot = cv2.getRotationMatrix2D(centro, 10, 1.0)rotada = cv2.warpAffine(img, M_rot, (w, h))mostrar_img(rotada, "Rotada 10°")
Traslación (Desplazamiento)
Una traslación desplaza todos los pixeles de la imagen una cantidad fija. Si movemos 50 px a la derecha y 30 px hacia abajo, entonces:
(x’ = x + 50)
(y’ = y + 30)
En OpenCV, esto se implementa con una matriz afín \(2\times 3\):